package scicalc;

import exception.ArraysLengthNotMatchException;
import exception.PowerNoMeaningException;
import exception.RandomGenerateException;

import java.util.*;

public class Calculate {

    // 横向计算
    public static final int AXIS_0 = 0;

    // 纵向计算
    public static final int AXIS_1 = 1;

    // 常量0
    public static final double ZERO = 0;

    // 常量1
    public static final double ONE = 1;

    // 常量e
    public static final double E = 2.7182818285;

    // 常量PI
    public static final double PI = 3.1415926546;

    /**
     * 生成一个随机数，[left,right)
     *
     * @param left
     * @param right
     * @return
     */
    public static int randomInt(int left, int right) {
        int r = 0;
        try {
            if (left >= right)
                throw new RandomGenerateException(left, right);
            Random random = new Random();
            r = random.nextInt(right - left) + left;
        } catch (RandomGenerateException e) {
            e.printStackTrace();
        }
        return r;
    }

    /**
     * 生成长度为n的随机数组，取值范围[left,right)
     *
     * @param left
     * @param right
     * @param n
     * @return
     */
    public static int[] randomIntArray(int left, int right, int n) {
        int[] rs = new int[n];
        try {
            if (n <= 0)
                throw new RandomGenerateException(n);
            else if (left >= right)
                throw new RandomGenerateException(left, right);
            else if (right - left < n)
                throw new RandomGenerateException(left, right, n);
            Set<Integer> set = new HashSet<>();
            int k = 0;
            while (k < n) {
                Random random = new Random();
                int r = random.nextInt(right - left) + left;
                if (set.add(r))
                    rs[k++] = r;
            }
        } catch (RandomGenerateException e) {
            e.printStackTrace();
        }
        return rs;
    }

    /**
     * 二维数组a与二维数组b对应位置相加
     *
     * @param a
     * @param b
     * @return
     */
    public static double[][] add(double[][] a, double[][] b) {
        double[][] rs = new double[a.length][a[0].length];
        try {
            if (a.length != b.length || a[0].length != b[0].length)
                throw new ArraysLengthNotMatchException(a[0].length, a.length, b[0].length, b.length);
            for (int i = 0; i < a.length; i++)
                for (int j = 0; j < a[i].length; j++)
                    rs[i][j] = a[i][j] + b[i][j];
        } catch (ArraysLengthNotMatchException e) {
            e.printStackTrace();
        }
        return rs;
    }

    /**
     * 一维数组a，b相减
     *
     * @param a
     * @param b
     * @return
     */
    public static double[] minus(double[] a, double[] b) {
        double[] rs = new double[a.length];
        try {
            if (a.length != b.length)
                throw new ArraysLengthNotMatchException(a.length, b.length);
            for (int i = 0; i < a.length; i++)
                rs[i] = a[i] - b[i];
        } catch (ArraysLengthNotMatchException e) {
            e.printStackTrace();
        }
        return rs;
    }

    /**
     * 二维数组a的每一行与一维数组b相减
     *
     * @param a
     * @param b
     * @return
     */
    public static double[][] minus(double[][] a, double[] b) {
        double[][] rs = new double[a.length][a[0].length];
        try {
            if (a[0].length != b.length)
                throw new ArraysLengthNotMatchException(a.length, b.length);
            for (int i = 0; i < a.length; i++)
                rs[i] = minus(a[i], b);
        } catch (ArraysLengthNotMatchException e) {
            e.printStackTrace();
        }
        return rs;
    }

    /**
     * 一维数组a与二维数组b每一行相减
     *
     * @param a
     * @return
     */
    public static double[][] minus(double[] a, double[][] b) {
        double[][] rs = new double[b.length][b[0].length];
        try {
            if (b[0].length != a.length)
                throw new ArraysLengthNotMatchException(a.length, b.length);
            for (int i = 0; i < b.length; i++)
                rs[i] = minus(a, b[i]);
        } catch (ArraysLengthNotMatchException e) {
            e.printStackTrace();
        }
        return rs;
    }

    /**
     * 二维数组a与二维数组b对应位置相减
     *
     * @param a
     * @param b
     * @return
     */
    public static double[][] minus(double[][] a, double[][] b) {
        double[][] rs = new double[a.length][a[0].length];
        try {
            if (a.length != b.length || a[0].length != b[0].length)
                throw new ArraysLengthNotMatchException(a[0].length, a.length, b[0].length, b.length);
            for (int i = 0; i < a.length; i++)
                for (int j = 0; j < a[i].length; j++)
                    rs[i][j] = a[i][j] - b[i][j];
        } catch (ArraysLengthNotMatchException e) {
            e.printStackTrace();
        }
        return rs;
    }

    /**
     * 判断a是否为整数
     *
     * @param a
     * @return
     */
    private static boolean isIntegerForDouble(double a) {
        double esp = 1e-10;
        return a - Math.floor(a) < esp;
    }

    /**
     * 求数base的exponent次方
     *
     * @param base
     * @param exponent
     * @return
     */
    public static double pow(double base, double exponent) {
        double r = 0;
        try {
            if (base == 0 && exponent <= 0)
                throw new PowerNoMeaningException(0);
            if (base < 0 && !isIntegerForDouble(exponent))
                throw new PowerNoMeaningException(-1);
            r = Math.pow(base, exponent);
        } catch (PowerNoMeaningException e) {
            e.printStackTrace();
        }
        return r;
    }

    /**
     * 求数组base中每一个数的exponent次方
     *
     * @param base
     * @param exponent
     * @return
     */
    public static double[] pow(double[] base, double exponent) {
        double[] rs = new double[base.length];
        for (int i = 0; i < base.length; i++)
            rs[i] = pow(base[i], exponent);
        return rs;
    }

    /**
     * 求二维数组base中每一个数的exponent次方
     *
     * @param base
     * @param exponent
     * @return
     */
    public static double[][] pow(double[][] base, double exponent) {
        double[][] rs = new double[base.length][base[0].length];
        for (int i = 0; i < base.length; i++)
            rs[i] = pow(base[i], exponent);
        return rs;
    }

    /**
     * 对a开平方
     *
     * @param a
     * @return
     */
    public static double sqrt(double a) {
        return pow(a, 0.5);
    }

    /**
     * 对数组a中所有元素开平方
     *
     * @param a
     * @return
     */
    public static double[] sqrt(double[] a) {
        return pow(a, 0.5);
    }

    /**
     * 对二维数组a中所有元素开平方
     *
     * @param a
     * @return
     */
    public static double[][] sqrt(double[][] a) {
        return pow(a, 0.5);
    }

    /**
     * 求数组中所有元素之和
     *
     * @param a
     * @return
     */
    public static double sum(double[] a) {
        double r = 0;
        for (double v : a)
            r += v;
        return r;
    }

    /**
     * 求二维数组中某一维度的所有元素之和
     *
     * @param a
     * @param axis
     * @return
     */
    public static double[] sum(double[][] a, int axis) {
        double[] rs = null;
        if (axis == AXIS_0) {
            rs = new double[a[0].length];
            for (int i = 0; i < rs.length; i++)
                for (int j = 0; j < a.length; j++)
                    rs[i] += a[j][i];
        } else if (axis == AXIS_1) {
            rs = new double[a.length];
            for (int i = 0; i < rs.length; i++)
                rs[i] = sum(a[i]);
        }
        return rs;
    }

    /**
     * 二维数组a所有元素之和
     *
     * @param a
     * @return
     */
    public static double sum(double[][] a) {
        double rs = 0;
        for (double[] line : a)
            for (double v : line)
                rs += v;
        return rs;
    }

    /**
     * 求数组中最小数的下标
     *
     * @param a
     * @return
     */
    public static int argmin(double... a) {
        int least = 0;
        for (int i = 0; i < a.length; i++)
            least = a[i] < a[least] ? i : least;
        return least;
    }

    /**
     * 求数组最小数的下标
     *
     * @param a
     * @return
     */
    public static int argmin(double[][] a) {
        double least = Double.MAX_VALUE;
        int r = 0;
        int index = 0;
        for (double[] line : a) {
            for (double i : line) {
                if (i < least) {
                    r = index;
                    least = i;
                }
                index++;
            }
        }
        return r;
    }

    /**
     * 求数组中最大数的下标
     *
     * @param a
     * @return
     */
    public static int argmax(double... a) {
        int largest = 0;
        for (int i = 0; i < a.length; i++)
            largest = a[i] > a[largest] ? i : largest;
        return largest;
    }

    /**
     * 生成下标为list的数组
     *
     * @param a
     * @param list
     * @return
     */
    public static double[] choose(double[] a, List<Integer> list) {
        double[] rs = new double[list.size()];
        for (int i = 0; i < list.size(); i++) {
            if (list.get(i) >= a.length)
                throw new ArrayIndexOutOfBoundsException();
            rs[i] = a[list.get(i)];
        }
        return rs;
    }

    /**
     * 生成下标为list对二维数组
     *
     * @param a
     * @param list
     * @return
     */
    public static double[][] choose(double[][] a, List<Integer> list) {
        double[][] rs = new double[list.size()][a[0].length];
        for (int i = 0; i < list.size(); i++) {
            if (list.get(i) >= a.length)
                throw new ArrayIndexOutOfBoundsException();
            for (int j = 0; j < a[list.get(i)].length; j++)
                rs[i][j] = a[list.get(i)][j];
        }
        return rs;
    }

    /**
     * 生成下标为b对数组
     *
     * @param a
     * @param b
     * @return
     */
    public static double[] choose(double[] a, int[] b) {
        double[] rs = new double[b.length];
        for (int i = 0; i < b.length; i++) {
            if (b[i] >= a.length)
                throw new ArrayIndexOutOfBoundsException();
            rs[i] = a[b[i]];
        }
        return rs;
    }

    /**
     * 生成下标为b的二维数组
     *
     * @param a
     * @param b
     * @return
     */
    public static double[][] choose(double[][] a, int[] b) {
        double[][] rs = new double[b.length][a[0].length];
        for (int i = 0; i < b.length; i++) {
            if (b[i] >= a.length)
                throw new ArrayIndexOutOfBoundsException();
            for (int j = 0; j < a[b[i]].length; j++)
                rs[i][j] = a[b[i]][j];
        }
        return rs;
    }

    /**
     * 数组a中所有数与b相除
     *
     * @param a
     * @param b
     * @return
     */
    public static double[] divide(double[] a, double b) {
        double[] rs = new double[a.length];
        if (b == 0)
            throw new ArithmeticException();
        for (int i = 0; i < a.length; i++)
            rs[i] = a[i] / b;
        return rs;
    }

    /**
     * 二维数组a中所有数与b相除
     *
     * @param a
     * @param b
     * @return
     */
    public static double[][] divide(double[][] a, double b) {
        double[][] rs = new double[a.length][a[0].length];
        for (int i = 0; i < a.length; i++)
            rs[i] = divide(a[i], b);
        return rs;
    }

    /**
     * 数组a中所有数与b相乘
     *
     * @param a
     * @param b
     * @return
     */
    public static double[] multi(double[] a, double b) {
        double[] rs = new double[a.length];
        for (int i = 0; i < a.length; i++)
            rs[i] = a[i] * b;
        return rs;
    }

    /**
     * 二维数组a中所有数与b相乘
     *
     * @param a
     * @param b
     * @return
     */
    public static double[][] multi(double[][] a, double b) {
        double[][] rs = new double[a.length][a[0].length];
        for (int i = 0; i < a.length; i++)
            rs[i] = multi(a[i], b);
        return rs;
    }

    /**
     * 数组求平均值
     *
     * @param a
     * @return
     */
    public static double mean(double[] a) {
        return sum(a) / a.length;
    }

    /**
     * 二维数组中某一维度的平均值
     *
     * @param a
     * @param axis
     * @return
     */
    public static double[] mean(double[][] a, int axis) {
        double[] rs = null;
        if (axis == AXIS_0)
            rs = divide(sum(a, axis), a.length);
        else if (axis == AXIS_1)
            rs = divide(sum(a, axis), a[0].length);
        return rs;
    }

    /**
     * 数组a中值为b的数的位置
     *
     * @param a
     * @param b
     * @return
     */
    public static List<Integer> where(int[] a, int b) {
        List<Integer> list = new ArrayList<>();
        for (int i = 0; i < a.length; i++)
            if (a[i] == b) list.add(i);
        return list;
    }

    /**
     * 自然常数e的a次方
     *
     * @param a
     * @return
     */
    public static double exp(double a) {
        return Calculate.pow(Calculate.E, a);
    }

    /**
     * 删除数组的第index行或列
     *
     * @param X
     * @param index
     * @param axis
     * @return
     */
    public static double[][] delete(double[][] X, int index, int axis) {
        double[][] rs = null;
        int lines = X.length;
        int rows = X[0].length;
        if (axis == AXIS_0) {
            rs = new double[lines - 1][rows];
            for (int i = 0; i < lines; i++) {
                if (i < index)
                    rs[i] = X[i];
                if (i > index)
                    rs[i - 1] = X[i];
            }
        } else if (axis == AXIS_1) {
            rs = new double[lines][rows - 1];
            for (int i = 0; i < lines; i++) {
                for (int j = 0; j < rows; j++) {
                    if (j < index)
                        rs[i][j] = X[i][j];
                    if (j > index)
                        rs[i][j - 1] = X[i][j];
                }
            }
        }
        return rs;
    }

    public static void print(double[][] a) {
        for (int i = 0; i < a.length; i++) {
            System.out.print(i == 0 ? "[" : " ");
            System.out.print("[");
            boolean notFirst = false;
            for (double v : a[i]) {
                if (notFirst) System.out.print(", ");
                notFirst = true;
                System.out.print(v);
            }
            System.out.print("]");
            if (i != a.length - 1) {
                System.out.println();
            }
        }
        System.out.println("]");
    }

    public static void print(double[] a) {
        System.out.print("[");
        boolean notFirst = false;
        for (double i : a) {
            if (notFirst) System.out.print(", ");
            notFirst = true;
            System.out.print(i);
        }
        System.out.println("]");
    }
}
